/*
* BinaryInputBuffer.java - This file is part of the Jakstab project.
*
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
* Copyright (C) 2003 The University of Arizona
*
* The original code for this class was taken from "MBEL: The Microsoft
* Bytecode Engineering Library" and modified for use with Jakstab.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab.util;
/**
* This is an input stream that buffers an entire binary file in advance.
* All multi-byte integers are read little-endian.
*
* @author Michael Stepp
* @author Johannes Kinder
*/
public abstract class BinaryInputBuffer {
protected int current;
/**
* Moves the file pointer to the given location.
*/
public void seek(long pos) throws java.io.IOException {
// skips to absolute location 'point' in the file
if (pos<0 || pos>=getSize())
throw new java.io.IOException("BinaryInputBuffer.seek: Seek position outside of file bounds: "+pos);
current = (int)pos;
}
/**
* Reads a null-terminated ASCII string from the file starting
* at the current location and ending at the next 0x00 ('\0') byte.
*/
public String readASCII() throws java.io.IOException {
String result = "";
int BYTE;
while((BYTE=readBYTE()) != 0)
result += (char)BYTE;
return result;
}
/**
* Returns the current file position of this input stream
*/
public long getCurrent() {
return current;
}
/**
* Asserts that the next bytes to be read from the input stream equal the
* byte array given. If sucessful, returns true and advances the file pointer by
* bytes.length. If unsuccessful, will either throw an exception (if premature EOF)
* or will returns false. The file position after an unsuccessful match is undefined,
* but will be somewhere between start and (start+bytes.length).
*/
public boolean match(byte[] bytes) throws java.io.IOException {
if ((current + bytes.length) > getSize())
return false;
for (int i=0;i<bytes.length;i++){
//if (bytes[i] != data[current++])
if (bytes[i] != (byte)readBYTE())
return false;
}
return true;
}
/**
* Advances the file pointer by 'amount'.
* @param amount the amount to skip, may be negative.
* @return true iff the skip was successful
*/
public boolean skip(int amount) {
if ((current + amount) > getSize() || (current + amount) < 0)
return false;
current += amount;
return true;
}
/**
* Moves the file pointer to the next higher multiple of 'a'.
* @param a the value to align to, must be positive
* @return true iff the align was successful
*/
public boolean align(int a) {
// skips to the next multiple of 'a' bytes
if (a<=0)
return false;
if (a==1)
return true;
int temp = current + (a-(current%a))%a;
if (temp>getSize())
return false;
current = temp;
return true;
}
/**
* Reads from the current file pointer into the given array.
* @param bytes the array to read into. this will attempt to read bytes.length bytes from the file
*/
public void read(byte[] bytes) throws java.io.IOException {
if (bytes==null)
return;
if ((current+bytes.length) > getSize())
throw new java.io.IOException("BinaryInputBuffer.read: Premature EOF");
for (int i=0; i<bytes.length; i++)
//bytes[i] = data[current++];
bytes[i] = (byte)readBYTE();
}
/**
* Reads an unsigned byte from the file, returned in the lower 8 bits of an int.
* Advances the file pointer by 1.
*/
public abstract int readBYTE() throws java.io.IOException;
/**
* Reads a single signed byte from the file without changing the file pointer.
*
* @param fp Address to read from
* @return a signed byte value
*/
public abstract byte getByteAt(int fp);
/**
* Reads an unsigned 2-byte integer from the file, returned in the lower 2 bytes of an int
* Advances the file pointer by 2.
*/
public int readWORD() throws java.io.IOException {
if (current+1>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readWORD: Premature EOF");
int b1 = readBYTE();
int b2 = readBYTE();
int result = ((b1 & 0xFF) | ((b2 & 0xFF) << 8));
return result;
}
/**
* Reads an unsigned 4-byte integer from the file and returns it the lower 4 bytes of a long.
* Advances the file pointer by 4.
*/
public long readDWORD() throws java.io.IOException {
if (current+3>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readDWORD: Premature EOF");
int b1 = readBYTE();
int b2 = readBYTE();
int b3 = readBYTE();
int b4 = readBYTE();
long result = ((b1 & 0xFFL) | ((b2 & 0xFFL) << 8)
| ((b3 & 0xFFL) << 16) | ((b4 & 0xFFL) << 24));
return result;
}
/**
* Reads an 8-byte (signed) quantity and returns it in a long.
* Advances the file pointer by 8.
*/
public long readDDWORD() throws java.io.IOException {
// throws java.io.IOException if EOF
if (current+7>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readDWORD: Premature EOF");
int b1 = readBYTE();
int b2 = readBYTE();
int b3 = readBYTE();
int b4 = readBYTE();
int b5 = readBYTE();
int b6 = readBYTE();
int b7 = readBYTE();
int b8 = readBYTE();
long result = ((b1 & 0xFFl) | ((b2 & 0xFFl) << 8)
| ((b3 & 0xFFl) << 16) | ((b4 & 0xFFl) << 24)
| ((b5 & 0xFFl) << 32) | ((b6 & 0xFFl) << 40)
| ((b7 & 0xFFl) << 48) | ((b8 & 0xFFl) << 56));
return result;
}
/**
* Reads an int64 (signed) from the file and returns it in a long.
* Advances the file pointer by 8.
*/
public long readINT64() throws java.io.IOException {
return readDDWORD();
}
/**
* Reads an int32 (signed) from the file and returns it in an int.
* Advances the file pointer by 4.
*/
public int readINT32() throws java.io.IOException {
if (current+3>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readINT32: Premature EOF");
int b1 = readBYTE();
int b2 = readBYTE();
int b3 = readBYTE();
int b4 = readBYTE();
int temp = (b1 & 0xFF) | ((b2 & 0xFF)<<8) |
((b3 & 0xFF)<<16) | ((b4 & 0xFF)<<24);
return temp;
}
/**
* Reads an int16 (signed) from the file and returns it in an int.
* Advances the file pointer by 2.
*/
public int readINT16() throws java.io.IOException {
short shorty=0;
if (current+1>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readINT16: Premature EOF");
int b1 = readBYTE();
int b2 = readBYTE();
shorty = (short)(((b1 & 0xFF) | (b2 & 0xFF)<<8) & 0xFFFF);
return shorty;
}
/**
* Reads an int8 (signed) from the file and returns it in an int.
* Advances the file pointer by 1.
*/
public int readINT8() throws java.io.IOException {
if (current>=getSize())
throw new java.io.IOException("BinaryInputBuffer.readINT8: Premature EOF");
return ((byte)readBYTE());
}
/**
* Reads an r4 from the file and returns it in a float.
* Advances the file pointer by 4.
*/
public float readR4() throws java.io.IOException {
return Float.intBitsToFloat((int)readDWORD());
}
/**
* Reads an r8 from the file and returns it in a double.
* Advances the file pointer by 8.
*/
public double readR8() throws java.io.IOException {
return Double.longBitsToDouble(readDDWORD());
}
public abstract long getSize();
}